Proyecto 02

Estudiante: Merian Herrera Fuentes

library("FactoMineR")
library("corrplot")
library("ggcorrplot")
library("corrr")
library("rattle")
library("fmsb")
library("ggmap")
library("mapproj")
library("factoextra")

   I Parte: Algoritmo de recomendación


1. [20 puntos] Se le pidió a un grupo de 100 clientes que compraron el mismo producto o productos similares en la tienda de AMAZON, que evaluaran el servicio recibido desde diferentes aspectos. El archivo EjemploAlgoritmosRecomendacion.csv muestra los promedios de la evaluación dada por los clientes en relación a las variables que se pueden apreciar en el encabezado de la tabla. Para este dataset, realice lo siguiente:

a) Cargue el dataset EjemploAlgoritmosRecomendacion.csv, verifique que la lectura de los datos es correcta.

clientes <- read.csv("EjemploAlgoritmosRecomendacion.csv", sep = ";", dec = ",", header = T,row.names = 1)
head(clientes)
        Velocidad.Entrega Precio Durabilidad Imagen.Producto Valor.Educativo Servicio.Retorno Tamano.Paquete
Adam                 2.05   0.30        3.45            2.35             2.4              2.3           2.60
Anna                 0.90   1.50        3.15            3.30             2.5              4.0           4.20
Bernard              1.70   2.60        2.85            3.00             4.3              2.7           4.10
Edward               1.35   0.50        3.55            2.95             1.8              2.3           3.90
Emilia               3.00   0.45        4.80            3.90             3.4              4.6           2.25
Fabian               0.95   1.65        3.95            2.40             2.6              1.9           4.85
        Calidad.Producto Numero.Estrellas
Adam                2.10              1.7
Anna                2.15              2.8
Bernard             2.60              3.3
Edward              1.95              1.7
Emilia              3.40              4.3
Fabian              2.20              3.0

b) A manera de diagnóstico, haga una inspección de los datos presentes en el dataset y describa las variables presentes, tipos de variables, si hay datos nulos.

# summary(clientes)
paste("Valores nulos para la variable Velocidad.Entrega:", sum(is.na(clientes$Velocidad.Entrega)))
paste("Valores nulos para la variable Precio:", sum(is.na(clientes$Precio)))
paste("Valores nulos para la variable Durabilidad:", sum(is.na(clientes$Durabilidad)))
paste("Valores nulos para la variable Imagen.Producto:", sum(is.na(clientes$Imagen.Producto)))
paste("Valores nulos para la variable Valor.Educativo:", sum(is.na(clientes$Valor.Educativo)))
paste("Valores nulos para la variable Servicio.Retorno:", sum(is.na(clientes$Servicio.Retorno)))
paste("Valores nulos para la variable Tamano.Paquete:", sum(is.na(clientes$Tamano.Paquete)))
paste("Valores nulos para la variable Calidad.Producto:", sum(is.na(clientes$Calidad.Producto)))
paste("Valores nulos para la variable Numero.Estrellas:", sum(is.na(clientes$Numero.Estrellas)))

En el dataset clientes podemos encontrar las siguientes variables:
   - Velocidad de Entrega
   - Precio
   - Durabilidad
   - Imagen del producto
   - Valor Educativo
   - Servicio de Retorno
   - Tamaño del paquete
   - Calidad del Producto
   - Número de estrellas.

Las cuales corresponden a valores númericos asignados por los clientes en orden de evaluar los productos comprados en AMAZON.
Todas estas variable son de tipo cuantitativo lo que significa que representan valores númericos y admiten operaciones matemáticas sobre ellas. A estas variables cuantitativas las podemos clasificar como continuas, es decir, pueden tomar cualquier valor en un intervalo dado.
Se aplicó la función is.na() sobre las columnas del dataset y no se encontrar datos nulos en el dataset clientes.

c) Determine si existen correlaciones importantes entre las variables del dataset, utilizando alguno de los gráficos o la matriz de correlaciones vistos en clases (si desea, puede usar otra herramienta de su preferencia). Recordemos que para aplicar un ACP, es importante que existan correlaciones entre las variables.

A continuación se muestran las correlaciones numericas para el dataset clientes:

# Para ver las correlaciones numéricas
correlaciones_numericas <- round(cor(clientes), 2)
correlaciones_numericas
                  Velocidad.Entrega Precio Durabilidad Imagen.Producto Valor.Educativo Servicio.Retorno Tamano.Paquete
Velocidad.Entrega              1.00  -0.35        0.51            0.05            0.61             0.08          -0.48
Precio                        -0.35   1.00       -0.49            0.27            0.51             0.19           0.47
Durabilidad                    0.51  -0.49        1.00           -0.12            0.07            -0.03          -0.45
Imagen.Producto                0.05   0.27       -0.12            1.00            0.30             0.79           0.20
Valor.Educativo                0.61   0.51        0.07            0.30            1.00             0.24          -0.06
Servicio.Retorno               0.08   0.19       -0.03            0.79            0.24             1.00           0.18
Tamano.Paquete                -0.48   0.47       -0.45            0.20           -0.06             0.18           1.00
Calidad.Producto               0.65   0.03        0.52            0.48            0.63             0.34          -0.28
Numero.Estrellas               0.68   0.08        0.56            0.22            0.70             0.26          -0.19
                  Calidad.Producto Numero.Estrellas
Velocidad.Entrega             0.65             0.68
Precio                        0.03             0.08
Durabilidad                   0.52             0.56
Imagen.Producto               0.48             0.22
Valor.Educativo               0.63             0.70
Servicio.Retorno              0.34             0.26
Tamano.Paquete               -0.28            -0.19
Calidad.Producto              1.00             0.71
Numero.Estrellas              0.71             1.00
corrplot(correlaciones_numericas)

ggcorrplot(correlaciones_numericas)

Las correlaciones positivas más fuertes que se observan son:
   - Imagen del producto con Servicio de Retorno
   - Número de estrellas con valor educativo
   - Número de estrellas con velocidad de entrega
   - Velocidad de entrega con calidad del producto

Las correlaciones negativas más fuertes que se observan son:
   - Precio con durabilidad
   - Tamaño del paquete con velocidad de entrega
   - Tamaño del paquete con durabilidad

d) Realice un ACP usando el paquete FactoMiner, creando el plano principal y el círculo de correlaciones. Superponga ambos gráficos e identifique la cantidad de clústeres que considere apropiado. Haga un análisis descriptivo de cada clúster, indique si hay variables correlacionadas y el tipo de correlación entre estas.

pca_clientes <- PCA(clientes, scale.unit=TRUE, ncp=5, graph = FALSE)
plot(pca_clientes, axes=c(1, 2), choix="ind", col.ind="purple",new.plot=TRUE) 

plot(pca_clientes, axes=c(1, 2), choix="var", col.var="blue",new.plot=TRUE) 

fviz_pca_biplot(pca_clientes, repel = FALSE,
                col.var = "#fc078a", # Variables color
                col.ind = "#696969"  # Individuals color
                ) 

Superponga ambos gráficos e identifique la cantidad de clústeres que considere apropiado. Haga un análisis descriptivo de cada clúster, indique si hay variables correlacionadas y el tipo de correlación entre estas.

Se pueden observar 2 clústeres en el análisis del PCA.
Clúster 01:
Clúster 02:

e) Repita el ejercicio anterior usando el paquete FactoMineR, elimine de los gráficos los individuos con \(cos^{2} (\theta)\) \(\le 0.2\) y las variables \(cos^{2} (\theta)\) \(\le 0.55\) y vuelva a crear el plano principal y el círculo de correlaciones. ¿Cuáles individuos quedaron mal representados?, ¿cuáles variables quedaron mal representadas?

Sugerencia: Puede usar el código del documento o cualquiera de su preferencia:

¿Cuáles individuos quedaron mal representados?

# El coseno cuadrado se calcula sumando los valores de las dos dimensiones por individuo o por variable (puede revisar la presentación, en las diapositivas finales se explica como se calcula este dato
# Se parte del hecho de que ya se tiene el modelo de ACP, que hemos llamado para efectos del ejemplo pca_clientes, de ahí se extrae los cosenos cuadrados de los individuos. Para el caso de las variables, debe cambiar la instrucción pca_datos$ind$cos2 por pca_clientes$var$cos2
cos2.ind<-(pca_clientes$ind$cos2[,1] + pca_clientes$ind$cos2[,2])
individuos_malrepresentados <- NULL
for (i in 1 : nrow(clientes)) {
  ifelse(cos2.ind [i] <= 0.2, individuos_malrepresentados <- rbind(individuos_malrepresentados, i), NA)   
}
cos2.ind[individuos_malrepresentados]
     Henry   Isabelle  Sebastian 
0.11569585 0.01262467 0.16038875 
#plot(pca_clientes, axes=c(1, 2), choix="ind", col.ind="red",new.plot=TRUE, select="cos2 0.2") 
#res.pca <- prcomp(clientes, scale = TRUE)
fviz_pca_ind(pca_clientes,
             col.ind = "cos2", # Color by the quality of representation
             gradient.cols = c("#00AFBB", "#E7B800", "#fc078a"),
             repel = FALSE     # Avoid text overlapping
             )

Los individuos que están mal representados son:
   - Henry
   - Isabelle
   - Sebastian
Ya que si calculamos el coseno cuadrado de sus coeficientes de relación todos los resultados son menores a 0.2

¿Cuáles variables quedaron mal representadas?

cos2.var<-(pca_clientes$var$cos2[,1] + pca_clientes$var$cos2[,2])
variables_malrepresentadas <- NULL
for (i in 1 : ncol(clientes)) {
  header <- names(cos2.var[i])[1]
  value <- cos2.ind[i]
 ifelse(value <= 0.55, variables_malrepresentadas <- cbind(matrix(c(header, value), 2, 1, TRUE), variables_malrepresentadas), NA)
}
variables_malrepresentadas
     [,1]               
[1,] "Servicio.Retorno" 
[2,] "0.454086771119677"
# plot(pca_clientes, axes=c(1, 2), choix="var", col.var="blue",new.plot=TRUE, select="cos2 0.55") 
fviz_pca_var(pca_clientes,
             col.var = "cos2", # Color by contributions to the PC
             gradient.cols = c("#00AFBB", "#E7B800", "#fc078a"),
             repel = TRUE     # Avoid text overlapping
             )

La única variable mal representada en los datos anteriores es Servicio.Retorno ya que el cálculo de el coseno cuadrado de su coeficiente de relación es menor a 0.55, como se puede observar en el gráfico anterior.

f) Realice un gráfico radar (araña) usando 3 clústeres e interprete cada uno de los clústeres.

modelo <- hclust(dist(clientes),method= "ward")
The "ward" method has been renamed to "ward.D"; note new "ward.D2"
centros<-centers.hclust(clientes,modelo,nclust=3,use.median=FALSE)
centros
     Velocidad.Entrega    Precio Durabilidad Imagen.Producto Valor.Educativo Servicio.Retorno Tamano.Paquete
[1,]          1.262791 1.1511628    3.503488        2.496512        2.358140         2.516279       3.748837
[2,]          1.700000 1.7090909    3.843182        3.006818        3.390909         3.040909       3.979545
[3,]          2.401429 0.8885714    4.557143        2.540000        3.302857         2.611429       2.851429
     Calidad.Producto Numero.Estrellas
[1,]         2.019767         2.288372
[2,]         2.590909         3.472727
[3,]         2.705714         3.891429
rownames(centros)<-c("Cluster 1","Cluster 2","Cluster 3")
centros<-as.data.frame(centros)
maximos<-apply(centros,2,max)
minimos<-apply(centros,2,min)
centros<-rbind(minimos,centros)
centros<-rbind(maximos,centros)
centros
          Velocidad.Entrega    Precio Durabilidad Imagen.Producto Valor.Educativo Servicio.Retorno Tamano.Paquete
1                  2.401429 1.7090909    4.557143        3.006818        3.390909         3.040909       3.979545
11                 1.262791 0.8885714    3.503488        2.496512        2.358140         2.516279       2.851429
Cluster 1          1.262791 1.1511628    3.503488        2.496512        2.358140         2.516279       3.748837
Cluster 2          1.700000 1.7090909    3.843182        3.006818        3.390909         3.040909       3.979545
Cluster 3          2.401429 0.8885714    4.557143        2.540000        3.302857         2.611429       2.851429
          Calidad.Producto Numero.Estrellas
1                 2.705714         3.891429
11                2.019767         2.288372
Cluster 1         2.019767         2.288372
Cluster 2         2.590909         3.472727
Cluster 3         2.705714         3.891429
radarchart(centros,maxmin=TRUE,axistype=4,axislabcol="slategray4",
           centerzero=FALSE,seg=8,cglcol="gray67",
           pcol=c("#00AFBB", "#E7B800", "#fc078a"),
           plty=1,
           plwd=5,
           title="Comparacion de clusteres")
legenda <-legend(1.5,1, legend=c("Cluster 1","Cluster 2","Cluster 3"),
                 seg.len=-1.4,
                 title="Clusteres",
                 pch=21, 
                 bty="n" ,lwd=3, y.intersp=1, horiz=FALSE,
                 col=c("#00AFBB", "#E7B800", "#fc078a"))

Cluster 1:
Cluster 2:
Cluster 3:

g) Suponga que se trabajaron los datos con 3 clústeres, como se muestra en el plano principal siguiente:


Partiendo del hecho de que todos los individuos compraron, ya sea el mismo producto o similares, pero, cada uno tuvo una experiencia de compra diferente que pudo ser mejor o peor en los diferentes rubros de la evaluación,

• ¿Qué productos recomendaría a Salome?

Respuesta

• ¿Qué productos recomendaría a Stephania?

Respuesta

• ¿Qué productos recomendaría a Lydia?

Respuesta

Es decir, los mismos productos que compró cuál otro cliente.

   II PARTE: resuelva la siguiente situación


1. [10 puntos] Considérese tabla datos ConsumoEuropa.csv que contiene una estimación del consumo promedio de proteínas, en gramos, por persona y por día, en Europa, datos del año 1981 (Está en el aula virtual con el nombre).

consumo <- read.csv("ConsumoEuropa.csv", sep = ";", dec = ",", header = T,row.names = 1)
head(consumo)
               CarneAnimal CarneCerdo Huevos Leche CarnePescado Cereales Almidon Semillas Frutas
Albania               10.1        1.4    0.5   8.9          0.2     42.3     0.6      5.5    1.7
Austria                8.9       14.0    4.3  19.9          2.1     28.0     3.6      1.3    4.3
Belgica               13.5        9.3    4.1  17.5          4.5     26.6     5.7      2.1    4.0
Bulgaria               7.8        6.0    1.6   8.3          1.2     56.7     1.1      3.7    4.2
Checoslovaquia         9.7       11.4    2.8  12.5          2.0     34.3     5.0      1.1    4.0
Dinamarca             10.6       10.8    3.7  25.0          9.9     21.9     4.8      0.7    2.4

a. Usando FactoMineR efectúe un ACP para esta tabla de datos.

pca_consumo <- PCA(consumo, scale.unit=TRUE, ncp=5, graph = FALSE)

b. Grafique el plano principal y el círculo de correlaciones, luego compare este Plano Principal con el mapa de Europa ¿Qué conclusiones puede sacar?

Considere que podría recortar alguno de estos gráficos, hacer rotaciones y es necesario buscar un mapa de Europa, de preferencia un mapa político.

plot(pca_consumo, axes=c(1, 2), choix="ind", col.ind="#00AFBB",new.plot=TRUE)

plot(pca_consumo, axes=c(1, 2), choix="var", col.var="#fc078a",new.plot=TRUE) 

LS0tCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCjxjZW50ZXI+PGltZyBzcmM9ImxvZ28ucG5nIiB3aWR0aD0iNTAwIi8+PC9jZW50ZXI+Cgo8Y2VudGVyPjxoMj48Yj5Qcm95ZWN0byAwMjxoMj48L2I+PC9jZW50ZXI+Cgo8aDQ+PGI+RXN0dWRpYW50ZTo8L2I+IE1lcmlhbiBIZXJyZXJhIEZ1ZW50ZXMgPGJyPjwvaDQ+CgpgYGB7ciAgbWVzc2FnZT1GQUxTRX0KbGlicmFyeSgiRmFjdG9NaW5lUiIpCmxpYnJhcnkoImNvcnJwbG90IikKbGlicmFyeSgiZ2djb3JycGxvdCIpCmxpYnJhcnkoImNvcnJyIikKbGlicmFyeSgicmF0dGxlIikKbGlicmFyeSgiZm1zYiIpCmxpYnJhcnkoImdnbWFwIikKbGlicmFyeSgibWFwcHJvaiIpCmxpYnJhcnkoImZhY3RvZXh0cmEiKQpgYGAKCgo8c3R5bGU+CmRpdi5ibHVlIHsgYmFja2dyb3VuZC1jb2xvcjojZTZmMGZmOyBib3JkZXItcmFkaXVzOiA1cHg7IHBhZGRpbmc6IDIwcHg7fQo8L3N0eWxlPgoKPGJyPgo8ZGl2IHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiMxRDI5NTE7Y29sb3I6d2hpdGU7cGFkZGluZzozcHg7bWFyZ2luZzoxMHB4OyI+CiAgPGg0PjxiPiZuYnNwOyZuYnNwOyZuYnNwO0kgUGFydGU6IEFsZ29yaXRtbyBkZSByZWNvbWVuZGFjacOzbjwvYj48L2g0Pgo8L2Rpdj4KPGJyPgoKPGRpdiBzdHlsZT0idGV4dC1hbGlnbjoganVzdGlmeTsiPgo8Yj4KMS4gWzIwIHB1bnRvc10gU2UgbGUgcGlkacOzIGEgdW4gZ3J1cG8gZGUgMTAwIGNsaWVudGVzIHF1ZSBjb21wcmFyb24gZWwgbWlzbW8gcHJvZHVjdG8gbyBwcm9kdWN0b3Mgc2ltaWxhcmVzIGVuIGxhIHRpZW5kYSBkZSBBTUFaT04sIHF1ZSBldmFsdWFyYW4gZWwgc2VydmljaW8gcmVjaWJpZG8gZGVzZGUgZGlmZXJlbnRlcyBhc3BlY3Rvcy4gRWwgYXJjaGl2byBFamVtcGxvQWxnb3JpdG1vc1JlY29tZW5kYWNpb24uY3N2IG11ZXN0cmEgbG9zIHByb21lZGlvcyBkZSBsYSBldmFsdWFjacOzbiBkYWRhIHBvciBsb3MgY2xpZW50ZXMgZW4gcmVsYWNpw7NuIGEgbGFzIHZhcmlhYmxlcyBxdWUgc2UgcHVlZGVuIGFwcmVjaWFyIGVuIGVsIGVuY2FiZXphZG8gZGUgbGEgdGFibGEuIFBhcmEgZXN0ZSBkYXRhc2V0LCByZWFsaWNlIGxvIHNpZ3VpZW50ZToKPGJyPjxicj4KPC9iPgoKPGI+CmEpIENhcmd1ZSBlbCBkYXRhc2V0IEVqZW1wbG9BbGdvcml0bW9zUmVjb21lbmRhY2lvbi5jc3YsIHZlcmlmaXF1ZSBxdWUgbGEgbGVjdHVyYSBkZSBsb3MgZGF0b3MgZXMgY29ycmVjdGEuIDxicj4KPC9iPgoKYGBge3J9CmNsaWVudGVzIDwtIHJlYWQuY3N2KCJFamVtcGxvQWxnb3JpdG1vc1JlY29tZW5kYWNpb24uY3N2Iiwgc2VwID0gIjsiLCBkZWMgPSAiLCIsIGhlYWRlciA9IFQscm93Lm5hbWVzID0gMSkKaGVhZChjbGllbnRlcykKYGBgCgo8Yj4KYikgQSBtYW5lcmEgZGUgZGlhZ27Ds3N0aWNvLCBoYWdhIHVuYSBpbnNwZWNjacOzbiBkZSBsb3MgZGF0b3MgcHJlc2VudGVzIGVuIGVsIGRhdGFzZXQgeSBkZXNjcmliYSBsYXMgdmFyaWFibGVzIHByZXNlbnRlcywgdGlwb3MgZGUgdmFyaWFibGVzLCBzaSBoYXkgZGF0b3MgbnVsb3MuIDxicj4KPC9iPgoKYGBge3J9CiMgc3VtbWFyeShjbGllbnRlcykKcGFzdGUoIlZhbG9yZXMgbnVsb3MgcGFyYSBsYSB2YXJpYWJsZSBWZWxvY2lkYWQuRW50cmVnYToiLCBzdW0oaXMubmEoY2xpZW50ZXMkVmVsb2NpZGFkLkVudHJlZ2EpKSkKcGFzdGUoIlZhbG9yZXMgbnVsb3MgcGFyYSBsYSB2YXJpYWJsZSBQcmVjaW86Iiwgc3VtKGlzLm5hKGNsaWVudGVzJFByZWNpbykpKQpwYXN0ZSgiVmFsb3JlcyBudWxvcyBwYXJhIGxhIHZhcmlhYmxlIER1cmFiaWxpZGFkOiIsIHN1bShpcy5uYShjbGllbnRlcyREdXJhYmlsaWRhZCkpKQpwYXN0ZSgiVmFsb3JlcyBudWxvcyBwYXJhIGxhIHZhcmlhYmxlIEltYWdlbi5Qcm9kdWN0bzoiLCBzdW0oaXMubmEoY2xpZW50ZXMkSW1hZ2VuLlByb2R1Y3RvKSkpCnBhc3RlKCJWYWxvcmVzIG51bG9zIHBhcmEgbGEgdmFyaWFibGUgVmFsb3IuRWR1Y2F0aXZvOiIsIHN1bShpcy5uYShjbGllbnRlcyRWYWxvci5FZHVjYXRpdm8pKSkKcGFzdGUoIlZhbG9yZXMgbnVsb3MgcGFyYSBsYSB2YXJpYWJsZSBTZXJ2aWNpby5SZXRvcm5vOiIsIHN1bShpcy5uYShjbGllbnRlcyRTZXJ2aWNpby5SZXRvcm5vKSkpCnBhc3RlKCJWYWxvcmVzIG51bG9zIHBhcmEgbGEgdmFyaWFibGUgVGFtYW5vLlBhcXVldGU6Iiwgc3VtKGlzLm5hKGNsaWVudGVzJFRhbWFuby5QYXF1ZXRlKSkpCnBhc3RlKCJWYWxvcmVzIG51bG9zIHBhcmEgbGEgdmFyaWFibGUgQ2FsaWRhZC5Qcm9kdWN0bzoiLCBzdW0oaXMubmEoY2xpZW50ZXMkQ2FsaWRhZC5Qcm9kdWN0bykpKQpwYXN0ZSgiVmFsb3JlcyBudWxvcyBwYXJhIGxhIHZhcmlhYmxlIE51bWVyby5Fc3RyZWxsYXM6Iiwgc3VtKGlzLm5hKGNsaWVudGVzJE51bWVyby5Fc3RyZWxsYXMpKSkKYGBgCgo+IEVuIGVsIGRhdGFzZXQgY2xpZW50ZXMgcG9kZW1vcyBlbmNvbnRyYXIgbGFzIHNpZ3VpZW50ZXMgdmFyaWFibGVzOiA8YnI+CiZuYnNwOyZuYnNwOyZuYnNwOy0gVmVsb2NpZGFkIGRlIEVudHJlZ2EgPGJyPgombmJzcDsmbmJzcDsmbmJzcDstIFByZWNpbyA8YnI+CiZuYnNwOyZuYnNwOyZuYnNwOy0gRHVyYWJpbGlkYWQgPGJyPgombmJzcDsmbmJzcDsmbmJzcDstIEltYWdlbiBkZWwgcHJvZHVjdG8gPGJyPgombmJzcDsmbmJzcDsmbmJzcDstIFZhbG9yIEVkdWNhdGl2byA8YnI+CiZuYnNwOyZuYnNwOyZuYnNwOy0gU2VydmljaW8gZGUgUmV0b3JubyA8YnI+CiZuYnNwOyZuYnNwOyZuYnNwOy0gVGFtYcOxbyBkZWwgcGFxdWV0ZSA8YnI+CiZuYnNwOyZuYnNwOyZuYnNwOy0gQ2FsaWRhZCBkZWwgUHJvZHVjdG8gPGJyPgombmJzcDsmbmJzcDsmbmJzcDstIE7Dum1lcm8gZGUgZXN0cmVsbGFzLiA8YnI+PGJyPgpMYXMgY3VhbGVzIGNvcnJlc3BvbmRlbiBhIHZhbG9yZXMgbsO6bWVyaWNvcyBhc2lnbmFkb3MgcG9yIGxvcyBjbGllbnRlcyBlbiBvcmRlbiBkZSBldmFsdWFyIGxvcyBwcm9kdWN0b3MgY29tcHJhZG9zIGVuIEFNQVpPTi4gPGJyPgpUb2RhcyBlc3RhcyB2YXJpYWJsZSBzb24gZGUgdGlwbyAqKmN1YW50aXRhdGl2byoqIGxvIHF1ZSBzaWduaWZpY2EgcXVlIHJlcHJlc2VudGFuIHZhbG9yZXMgbsO6bWVyaWNvcyB5IGFkbWl0ZW4gb3BlcmFjaW9uZXMgbWF0ZW3DoXRpY2FzIHNvYnJlIGVsbGFzLiBBIGVzdGFzIHZhcmlhYmxlcyBjdWFudGl0YXRpdmFzIGxhcyBwb2RlbW9zIGNsYXNpZmljYXIgY29tbyAqKmNvbnRpbnVhcyoqLCBlcyBkZWNpciwgcHVlZGVuIHRvbWFyIGN1YWxxdWllciB2YWxvciBlbiB1biBpbnRlcnZhbG8gZGFkby4gPGJyPgpTZSBhcGxpY8OzIGxhIGZ1bmNpw7NuIGlzLm5hKCkgc29icmUgbGFzIGNvbHVtbmFzIGRlbCBkYXRhc2V0IHkgbm8gc2UgZW5jb250cmFyIGRhdG9zIG51bG9zIGVuIGVsIGRhdGFzZXQgY2xpZW50ZXMuCgo8Yj4KYykgRGV0ZXJtaW5lIHNpIGV4aXN0ZW4gY29ycmVsYWNpb25lcyBpbXBvcnRhbnRlcyBlbnRyZSBsYXMgdmFyaWFibGVzIGRlbCBkYXRhc2V0LCB1dGlsaXphbmRvIGFsZ3VubyBkZSBsb3MgZ3LDoWZpY29zIG8gbGEgbWF0cml6IGRlIGNvcnJlbGFjaW9uZXMgdmlzdG9zIGVuIGNsYXNlcyAoc2kgIGRlc2VhLCBwdWVkZSB1c2FyIG90cmEgaGVycmFtaWVudGEgZGUgc3UgcHJlZmVyZW5jaWEpLiBSZWNvcmRlbW9zIHF1ZSBwYXJhIGFwbGljYXIgdW4gQUNQLCBlcyBpbXBvcnRhbnRlIHF1ZSBleGlzdGFuIGNvcnJlbGFjaW9uZXMgZW50cmUgbGFzIHZhcmlhYmxlcy4gPGJyPgo8L2I+Cjxicj4KQSBjb250aW51YWNpw7NuIHNlIG11ZXN0cmFuIGxhcyBjb3JyZWxhY2lvbmVzIG51bWVyaWNhcyBwYXJhIGVsIGRhdGFzZXQgY2xpZW50ZXM6CmBgYHtyfQojIFBhcmEgdmVyIGxhcyBjb3JyZWxhY2lvbmVzIG51bcOpcmljYXMKY29ycmVsYWNpb25lc19udW1lcmljYXMgPC0gcm91bmQoY29yKGNsaWVudGVzKSwgMikKY29ycmVsYWNpb25lc19udW1lcmljYXMKYGBgCgpgYGB7cn0KY29ycnBsb3QoY29ycmVsYWNpb25lc19udW1lcmljYXMpCmBgYAoKYGBge3J9CmdnY29ycnBsb3QoY29ycmVsYWNpb25lc19udW1lcmljYXMpCmBgYAoKPiBMYXMgY29ycmVsYWNpb25lcyAqKnBvc2l0aXZhcyoqIG3DoXMgZnVlcnRlcyBxdWUgc2Ugb2JzZXJ2YW4gc29uOiA8YnI+CiZuYnNwOyZuYnNwOyZuYnNwOy0gKipJbWFnZW4gZGVsIHByb2R1Y3RvKiogIGNvbiAqKlNlcnZpY2lvIGRlIFJldG9ybm8qKiA8YnI+CiZuYnNwOyZuYnNwOyZuYnNwOy0gKipOw7ptZXJvIGRlIGVzdHJlbGxhcyoqIGNvbiAqKnZhbG9yIGVkdWNhdGl2byoqIDxicj4KJm5ic3A7Jm5ic3A7Jm5ic3A7LSAqKk7Dum1lcm8gZGUgZXN0cmVsbGFzKiogY29uICoqdmVsb2NpZGFkIGRlIGVudHJlZ2EqKiA8YnI+CiZuYnNwOyZuYnNwOyZuYnNwOy0gKipWZWxvY2lkYWQgZGUgZW50cmVnYSoqIGNvbiAqKmNhbGlkYWQgZGVsIHByb2R1Y3RvKiogPGJyPjxicj4KTGFzIGNvcnJlbGFjaW9uZXMgKipuZWdhdGl2YXMqKiBtw6FzIGZ1ZXJ0ZXMgcXVlIHNlIG9ic2VydmFuIHNvbjogPGJyPgombmJzcDsmbmJzcDsmbmJzcDstICoqUHJlY2lvKiogY29uICoqZHVyYWJpbGlkYWQqKiA8YnI+CiZuYnNwOyZuYnNwOyZuYnNwOy0gKipUYW1hw7FvIGRlbCBwYXF1ZXRlKiogY29uICoqdmVsb2NpZGFkIGRlIGVudHJlZ2EqKiA8YnI+CiZuYnNwOyZuYnNwOyZuYnNwOy0gKipUYW1hw7FvIGRlbCBwYXF1ZXRlKiogY29uICoqZHVyYWJpbGlkYWQqKiA8YnI+Cgo8Yj4KZCkgUmVhbGljZSB1biBBQ1AgdXNhbmRvIGVsIHBhcXVldGUgRmFjdG9NaW5lciwgY3JlYW5kbyBlbCBwbGFubyBwcmluY2lwYWwgeSBlbCBjw61yY3VsbyBkZSBjb3JyZWxhY2lvbmVzLiBTdXBlcnBvbmdhIGFtYm9zIGdyw6FmaWNvcyBlIGlkZW50aWZpcXVlIGxhIGNhbnRpZGFkIGRlIGNsw7pzdGVyZXMgcXVlIGNvbnNpZGVyZSBhcHJvcGlhZG8uIEhhZ2EgdW4gYW7DoWxpc2lzIGRlc2NyaXB0aXZvIGRlIGNhZGEgY2zDunN0ZXIsIGluZGlxdWUgc2kgaGF5IHZhcmlhYmxlcyBjb3JyZWxhY2lvbmFkYXMgeSBlbCB0aXBvIGRlIGNvcnJlbGFjacOzbiBlbnRyZSBlc3Rhcy4gPGJyPgo8L2I+CgpgYGB7cn0KcGNhX2NsaWVudGVzIDwtIFBDQShjbGllbnRlcywgc2NhbGUudW5pdD1UUlVFLCBuY3A9NSwgZ3JhcGggPSBGQUxTRSkKcGxvdChwY2FfY2xpZW50ZXMsIGF4ZXM9YygxLCAyKSwgY2hvaXg9ImluZCIsIGNvbC5pbmQ9InB1cnBsZSIsbmV3LnBsb3Q9VFJVRSkgCmBgYAoKYGBge3J9CnBsb3QocGNhX2NsaWVudGVzLCBheGVzPWMoMSwgMiksIGNob2l4PSJ2YXIiLCBjb2wudmFyPSJibHVlIixuZXcucGxvdD1UUlVFKSAKYGBgCgpgYGB7cn0KZnZpel9wY2FfYmlwbG90KHBjYV9jbGllbnRlcywgcmVwZWwgPSBGQUxTRSwKICAgICAgICAgICAgICAgIGNvbC52YXIgPSAiI2ZjMDc4YSIsICMgVmFyaWFibGVzIGNvbG9yCiAgICAgICAgICAgICAgICBjb2wuaW5kID0gIiM2OTY5NjkiICAjIEluZGl2aWR1YWxzIGNvbG9yCiAgICAgICAgICAgICAgICApIApgYGAKClN1cGVycG9uZ2EgYW1ib3MgZ3LDoWZpY29zIGUgaWRlbnRpZmlxdWUgbGEgY2FudGlkYWQgZGUgY2zDunN0ZXJlcyBxdWUgY29uc2lkZXJlIGFwcm9waWFkby4gSGFnYSB1biBhbsOhbGlzaXMgZGVzY3JpcHRpdm8gZGUgY2FkYSBjbMO6c3RlciwgaW5kaXF1ZSBzaSBoYXkgdmFyaWFibGVzIGNvcnJlbGFjaW9uYWRhcyB5IGVsIHRpcG8gZGUgY29ycmVsYWNpw7NuIGVudHJlIGVzdGFzLgoKPiBTZSBwdWVkZW4gb2JzZXJ2YXIgKioyIGNsw7pzdGVyZXMqKiBlbiBlbCBhbsOhbGlzaXMgZGVsIFBDQS4gPGJyPgoqKkNsw7pzdGVyIDAxOioqIDxicj4KKipDbMO6c3RlciAwMjoqKiA8YnI+Cgo8Yj4KZSkgUmVwaXRhIGVsIGVqZXJjaWNpbyBhbnRlcmlvciB1c2FuZG8gZWwgcGFxdWV0ZSBGYWN0b01pbmVSLCBlbGltaW5lIGRlIGxvcyBncsOhZmljb3MgbG9zIGluZGl2aWR1b3MgY29uICRjb3NeezJ9IChcdGhldGEpJCAkXGxlIDAuMiQgIHkgbGFzIHZhcmlhYmxlcyAkY29zXnsyfSAoXHRoZXRhKSQgJFxsZSAwLjU1JCB5IHZ1ZWx2YSBhIGNyZWFyIGVsIHBsYW5vIHByaW5jaXBhbCB5IGVsIGPDrXJjdWxvIGRlIGNvcnJlbGFjaW9uZXMuIMK/Q3XDoWxlcyBpbmRpdmlkdW9zIHF1ZWRhcm9uIG1hbCByZXByZXNlbnRhZG9zPywgwr9jdcOhbGVzIHZhcmlhYmxlcyBxdWVkYXJvbiBtYWwgcmVwcmVzZW50YWRhcz8gPGJyPgoKU3VnZXJlbmNpYTogUHVlZGUgdXNhciBlbCBjw7NkaWdvIGRlbCBkb2N1bWVudG8gbyBjdWFscXVpZXJhIGRlIHN1IHByZWZlcmVuY2lhOiA8YnI+CjwvYj4KPC9kaXY+Cgo8ZGl2IHN0eWxlPSJ0ZXh0LWFsaWduOiBqdXN0aWZ5OyI+Cioqwr9DdcOhbGVzIGluZGl2aWR1b3MgcXVlZGFyb24gbWFsIHJlcHJlc2VudGFkb3M/Kio8YnI+CgpgYGB7cn0KIyBFbCBjb3Nlbm8gY3VhZHJhZG8gc2UgY2FsY3VsYSBzdW1hbmRvIGxvcyB2YWxvcmVzIGRlIGxhcyBkb3MgZGltZW5zaW9uZXMgcG9yIGluZGl2aWR1byBvIHBvciB2YXJpYWJsZSAocHVlZGUgcmV2aXNhciBsYSBwcmVzZW50YWNpw7NuLCBlbiBsYXMgZGlhcG9zaXRpdmFzIGZpbmFsZXMgc2UgZXhwbGljYSBjb21vIHNlIGNhbGN1bGEgZXN0ZSBkYXRvCgojIFNlIHBhcnRlIGRlbCBoZWNobyBkZSBxdWUgeWEgc2UgdGllbmUgZWwgbW9kZWxvIGRlIEFDUCwgcXVlIGhlbW9zIGxsYW1hZG8gcGFyYSBlZmVjdG9zIGRlbCBlamVtcGxvIHBjYV9jbGllbnRlcywgZGUgYWjDrSBzZSBleHRyYWUgbG9zIGNvc2Vub3MgY3VhZHJhZG9zIGRlIGxvcyBpbmRpdmlkdW9zLiBQYXJhIGVsIGNhc28gZGUgbGFzIHZhcmlhYmxlcywgZGViZSBjYW1iaWFyIGxhIGluc3RydWNjacOzbiBwY2FfZGF0b3MkaW5kJGNvczIgcG9yIHBjYV9jbGllbnRlcyR2YXIkY29zMgpjb3MyLmluZDwtKHBjYV9jbGllbnRlcyRpbmQkY29zMlssMV0gKyBwY2FfY2xpZW50ZXMkaW5kJGNvczJbLDJdKQoKaW5kaXZpZHVvc19tYWxyZXByZXNlbnRhZG9zIDwtIE5VTEwKCmZvciAoaSBpbiAxIDogbnJvdyhjbGllbnRlcykpIHsKICBpZmVsc2UoY29zMi5pbmQgW2ldIDw9IDAuMiwgaW5kaXZpZHVvc19tYWxyZXByZXNlbnRhZG9zIDwtIHJiaW5kKGluZGl2aWR1b3NfbWFscmVwcmVzZW50YWRvcywgaSksIE5BKSAgIAp9Cgpjb3MyLmluZFtpbmRpdmlkdW9zX21hbHJlcHJlc2VudGFkb3NdCgpgYGAKCmBgYHtyfQojcGxvdChwY2FfY2xpZW50ZXMsIGF4ZXM9YygxLCAyKSwgY2hvaXg9ImluZCIsIGNvbC5pbmQ9InJlZCIsbmV3LnBsb3Q9VFJVRSwgc2VsZWN0PSJjb3MyIDAuMiIpIApgYGAKCmBgYHtyfQojcmVzLnBjYSA8LSBwcmNvbXAoY2xpZW50ZXMsIHNjYWxlID0gVFJVRSkKZnZpel9wY2FfaW5kKHBjYV9jbGllbnRlcywKICAgICAgICAgICAgIGNvbC5pbmQgPSAiY29zMiIsICMgQ29sb3IgYnkgdGhlIHF1YWxpdHkgb2YgcmVwcmVzZW50YXRpb24KICAgICAgICAgICAgIGdyYWRpZW50LmNvbHMgPSBjKCIjMDBBRkJCIiwgIiNFN0I4MDAiLCAiI2ZjMDc4YSIpLAogICAgICAgICAgICAgcmVwZWwgPSBGQUxTRSAgICAgIyBBdm9pZCB0ZXh0IG92ZXJsYXBwaW5nCiAgICAgICAgICAgICApCmBgYAoKPiBMb3MgaW5kaXZpZHVvcyBxdWUgZXN0w6FuIG1hbCByZXByZXNlbnRhZG9zIHNvbjo8YnI+CiZuYnNwOyZuYnNwOyZuYnNwOy0gSGVucnkgPGJyPgombmJzcDsmbmJzcDsmbmJzcDstIElzYWJlbGxlIDxicj4KJm5ic3A7Jm5ic3A7Jm5ic3A7LSBTZWJhc3RpYW4gPGJyPgpZYSBxdWUgc2kgY2FsY3VsYW1vcyBlbCBjb3Nlbm8gY3VhZHJhZG8gZGUgc3VzIGNvZWZpY2llbnRlcyBkZSByZWxhY2nDs24gdG9kb3MgbG9zIHJlc3VsdGFkb3Mgc29uIG1lbm9yZXMgYSAwLjIKCioqwr9DdcOhbGVzIHZhcmlhYmxlcyBxdWVkYXJvbiBtYWwgcmVwcmVzZW50YWRhcz8qKgoKYGBge3J9CmNvczIudmFyPC0ocGNhX2NsaWVudGVzJHZhciRjb3MyWywxXSArIHBjYV9jbGllbnRlcyR2YXIkY29zMlssMl0pCgp2YXJpYWJsZXNfbWFscmVwcmVzZW50YWRhcyA8LSBOVUxMCmZvciAoaSBpbiAxIDogbmNvbChjbGllbnRlcykpIHsKICBoZWFkZXIgPC0gbmFtZXMoY29zMi52YXJbaV0pWzFdCiAgdmFsdWUgPC0gY29zMi5pbmRbaV0KIGlmZWxzZSh2YWx1ZSA8PSAwLjU1LCB2YXJpYWJsZXNfbWFscmVwcmVzZW50YWRhcyA8LSBjYmluZChtYXRyaXgoYyhoZWFkZXIsIHZhbHVlKSwgMiwgMSwgVFJVRSksIHZhcmlhYmxlc19tYWxyZXByZXNlbnRhZGFzKSwgTkEpCn0KdmFyaWFibGVzX21hbHJlcHJlc2VudGFkYXMKYGBgCgpgYGB7cn0KIyBwbG90KHBjYV9jbGllbnRlcywgYXhlcz1jKDEsIDIpLCBjaG9peD0idmFyIiwgY29sLnZhcj0iYmx1ZSIsbmV3LnBsb3Q9VFJVRSwgc2VsZWN0PSJjb3MyIDAuNTUiKSAKYGBgCgpgYGB7cn0KZnZpel9wY2FfdmFyKHBjYV9jbGllbnRlcywKICAgICAgICAgICAgIGNvbC52YXIgPSAiY29zMiIsICMgQ29sb3IgYnkgY29udHJpYnV0aW9ucyB0byB0aGUgUEMKICAgICAgICAgICAgIGdyYWRpZW50LmNvbHMgPSBjKCIjMDBBRkJCIiwgIiNFN0I4MDAiLCAiI2ZjMDc4YSIpLAogICAgICAgICAgICAgcmVwZWwgPSBUUlVFICAgICAjIEF2b2lkIHRleHQgb3ZlcmxhcHBpbmcKICAgICAgICAgICAgICkKYGBgCgo+IExhIMO6bmljYSB2YXJpYWJsZSBtYWwgcmVwcmVzZW50YWRhIGVuIGxvcyBkYXRvcyBhbnRlcmlvcmVzIGVzICoqU2VydmljaW8uUmV0b3JubyoqIHlhIHF1ZSBlbCBjw6FsY3VsbyBkZSBlbCBjb3Nlbm8gY3VhZHJhZG8gZGUgc3UgY29lZmljaWVudGUgZGUgcmVsYWNpw7NuIGVzIG1lbm9yIGEgMC41NSwgY29tbyBzZSBwdWVkZSBvYnNlcnZhciBlbiBlbCBncsOhZmljbyBhbnRlcmlvci4KCgo8L2Rpdj4KCjxkaXYgc3R5bGU9InRleHQtYWxpZ246IGp1c3RpZnk7Ij4KPGI+CmYpIFJlYWxpY2UgdW4gZ3LDoWZpY28gcmFkYXIgKGFyYcOxYSkgdXNhbmRvIDMgY2zDunN0ZXJlcyBlIGludGVycHJldGUgY2FkYSB1bm8gZGUgbG9zIGNsw7pzdGVyZXMuIDxicj4KPC9iPgoKYGBge3J9Cm1vZGVsbyA8LSBoY2x1c3QoZGlzdChjbGllbnRlcyksbWV0aG9kPSAid2FyZCIpCmBgYApgYGB7cn0KY2VudHJvczwtY2VudGVycy5oY2x1c3QoY2xpZW50ZXMsbW9kZWxvLG5jbHVzdD0zLHVzZS5tZWRpYW49RkFMU0UpCmNlbnRyb3MKYGBgCgpgYGB7cn0Kcm93bmFtZXMoY2VudHJvcyk8LWMoIkNsdXN0ZXIgMSIsIkNsdXN0ZXIgMiIsIkNsdXN0ZXIgMyIpCmNlbnRyb3M8LWFzLmRhdGEuZnJhbWUoY2VudHJvcykKbWF4aW1vczwtYXBwbHkoY2VudHJvcywyLG1heCkKbWluaW1vczwtYXBwbHkoY2VudHJvcywyLG1pbikKY2VudHJvczwtcmJpbmQobWluaW1vcyxjZW50cm9zKQpjZW50cm9zPC1yYmluZChtYXhpbW9zLGNlbnRyb3MpCmNlbnRyb3MKYGBgCgpgYGB7cn0KcmFkYXJjaGFydChjZW50cm9zLG1heG1pbj1UUlVFLGF4aXN0eXBlPTQsYXhpc2xhYmNvbD0ic2xhdGVncmF5NCIsCiAgICAgICAgICAgY2VudGVyemVybz1GQUxTRSxzZWc9OCxjZ2xjb2w9ImdyYXk2NyIsCiAgICAgICAgICAgcGNvbD1jKCIjMDBBRkJCIiwgIiNFN0I4MDAiLCAiI2ZjMDc4YSIpLAogICAgICAgICAgIHBsdHk9MSwKICAgICAgICAgICBwbHdkPTUsCiAgICAgICAgICAgdGl0bGU9IkNvbXBhcmFjaW9uIGRlIGNsdXN0ZXJlcyIpCgpsZWdlbmRhIDwtbGVnZW5kKDEuNSwxLCBsZWdlbmQ9YygiQ2x1c3RlciAxIiwiQ2x1c3RlciAyIiwiQ2x1c3RlciAzIiksCiAgICAgICAgICAgICAgICAgc2VnLmxlbj0tMS40LAogICAgICAgICAgICAgICAgIHRpdGxlPSJDbHVzdGVyZXMiLAogICAgICAgICAgICAgICAgIHBjaD0yMSwgCiAgICAgICAgICAgICAgICAgYnR5PSJuIiAsbHdkPTMsIHkuaW50ZXJzcD0xLCBob3Jpej1GQUxTRSwKICAgICAgICAgICAgICAgICBjb2w9YygiIzAwQUZCQiIsICIjRTdCODAwIiwgIiNmYzA3OGEiKSkKYGBgCgo+IENsdXN0ZXIgMTogPGJyPgpDbHVzdGVyIDI6IDxicj4KQ2x1c3RlciAzOiA8YnI+Cgo8Yj4KZykgU3Vwb25nYSBxdWUgc2UgdHJhYmFqYXJvbiBsb3MgZGF0b3MgY29uIDMgY2zDunN0ZXJlcywgY29tbyBzZSBtdWVzdHJhIGVuIGVsIHBsYW5vIHByaW5jaXBhbCBzaWd1aWVudGU6IDxicj4KCjxjZW50ZXI+PGltZyBzcmM9InAyX2dyYWZpY29zLnBuZyIgd2lkdGg9IjEwMDAiLz48L2NlbnRlcj4KCjxicj4KUGFydGllbmRvIGRlbCBoZWNobyBkZSBxdWUgdG9kb3MgbG9zIGluZGl2aWR1b3MgY29tcHJhcm9uLCB5YSBzZWEgZWwgbWlzbW8gcHJvZHVjdG8gbyBzaW1pbGFyZXMsIHBlcm8sIGNhZGEgdW5vIHR1dm8gdW5hIGV4cGVyaWVuY2lhIGRlIGNvbXByYSBkaWZlcmVudGUgcXVlIHB1ZG8gc2VyIG1lam9yIG8gcGVvciBlbiBsb3MgZGlmZXJlbnRlcyBydWJyb3MgZGUgbGEgZXZhbHVhY2nDs24sIDxicj4KCuKAoiDCv1F1w6kgcHJvZHVjdG9zIHJlY29tZW5kYXLDrWEgYSBTYWxvbWU/PGJyPgo8L2I+Cgo+IFJlc3B1ZXN0YQoKPGI+CuKAoiDCv1F1w6kgcHJvZHVjdG9zIHJlY29tZW5kYXLDrWEgYSBTdGVwaGFuaWE/PGJyPgo8L2I+Cgo+IFJlc3B1ZXN0YQoKPGI+CuKAoiDCv1F1w6kgcHJvZHVjdG9zIHJlY29tZW5kYXLDrWEgYSBMeWRpYT88YnI+CjwvYj4KCj4gUmVzcHVlc3RhCgo8Yj4KRXMgZGVjaXIsIGxvcyBtaXNtb3MgcHJvZHVjdG9zIHF1ZSBjb21wcsOzIGN1w6FsIG90cm8gY2xpZW50ZS4KPC9iPgo8L2Rpdj4KCgo8ZGl2IHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiMxRDI5NTE7Y29sb3I6d2hpdGU7cGFkZGluZzozcHg7bWFyZ2luZzoxMHB4OyI+CiAgPGg0PjxiPiZuYnNwOyZuYnNwOyZuYnNwO0lJIFBBUlRFOiByZXN1ZWx2YSBsYSBzaWd1aWVudGUgc2l0dWFjacOzbjwvYj48L2g0Pgo8L2Rpdj4KPGJyPgoKPGRpdiBzdHlsZT0idGV4dC1hbGlnbjoganVzdGlmeTsiPgo8Yj4KMS4gWzEwIHB1bnRvc10gQ29uc2lkw6lyZXNlIHRhYmxhIGRhdG9zIENvbnN1bW9FdXJvcGEuY3N2IHF1ZSBjb250aWVuZSB1bmEgZXN0aW1hY2nDs24gZGVsIGNvbnN1bW8gcHJvbWVkaW8gZGUgcHJvdGXDrW5hcywgZW4gZ3JhbW9zLCBwb3IgcGVyc29uYSB5IHBvciBkw61hLCBlbiBFdXJvcGEsIGRhdG9zIGRlbCBhw7FvIDE5ODEgKEVzdMOhIGVuIGVsIGF1bGEgdmlydHVhbCBjb24gZWwgbm9tYnJlKS4gPGJyPjxicj4KPC9iPgoKYGBge3J9CmNvbnN1bW8gPC0gcmVhZC5jc3YoIkNvbnN1bW9FdXJvcGEuY3N2Iiwgc2VwID0gIjsiLCBkZWMgPSAiLCIsIGhlYWRlciA9IFQscm93Lm5hbWVzID0gMSkKaGVhZChjb25zdW1vKQpgYGAKCjxiPgphLiBVc2FuZG8gRmFjdG9NaW5lUiBlZmVjdMO6ZSB1biBBQ1AgcGFyYSBlc3RhIHRhYmxhIGRlIGRhdG9zLjxicj4KPC9iPgpgYGB7cn0KcGNhX2NvbnN1bW8gPC0gUENBKGNvbnN1bW8sIHNjYWxlLnVuaXQ9VFJVRSwgbmNwPTUsIGdyYXBoID0gRkFMU0UpCmBgYAoKCjxiPgpiLiBHcmFmaXF1ZSBlbCBwbGFubyBwcmluY2lwYWwgeSBlbCBjw61yY3VsbyBkZSBjb3JyZWxhY2lvbmVzLCBsdWVnbyBjb21wYXJlIGVzdGUgUGxhbm8gUHJpbmNpcGFsIGNvbiBlbCBtYXBhIGRlIEV1cm9wYSDCv1F1w6kgY29uY2x1c2lvbmVzIHB1ZWRlIHNhY2FyPzxicj48YnI+CgpDb25zaWRlcmUgcXVlIHBvZHLDrWEgcmVjb3J0YXIgYWxndW5vIGRlIGVzdG9zIGdyw6FmaWNvcywgaGFjZXIgcm90YWNpb25lcyB5IGVzIG5lY2VzYXJpbyBidXNjYXIgdW4gbWFwYSBkZSBFdXJvcGEsIGRlIHByZWZlcmVuY2lhIHVuIG1hcGEgcG9sw610aWNvLgoKPC9iPgoKYGBge3J9CnBsb3QocGNhX2NvbnN1bW8sIGF4ZXM9YygxLCAyKSwgY2hvaXg9ImluZCIsIGNvbC5pbmQ9IiMwMEFGQkIiLG5ldy5wbG90PVRSVUUpCmBgYAoKYGBge3J9CnBsb3QocGNhX2NvbnN1bW8sIGF4ZXM9YygxLCAyKSwgY2hvaXg9InZhciIsIGNvbC52YXI9IiNmYzA3OGEiLG5ldy5wbG90PVRSVUUpIApgYGAKCjxjZW50ZXI+PGltZyBzcmM9Im1hcGFldXJvcGEuZ2lmIiB3aWR0aD0iNjAwIi8+PC9jZW50ZXI+Cgo8L2Rpdj4=